/*
 * CG711Encoder.cpp
 *
 *  Created on: 2017年2月17日
 *      Author: chuanjiang.zh
 */

#include "CG711Encoder.h"
#include "AVFramePtr.h"
#include "DateTime.h"


namespace av
{

G711Encoder::G711Encoder():
		m_profile(),
		m_codecContext(),
		m_swrContext()
{
}

G711Encoder::~G711Encoder()
{
	close();
}

int G711Encoder::open(const MediaProfile& profile)
{
	m_profile = profile;

	int rc = 0;
	if (m_profile.checkAudio())
	{
		rc = openEncoder(m_profile);
	}
	return rc;
}

void G711Encoder::close()
{
	closeEncoder();
}

bool G711Encoder::isOpen()
{
	return isEncoderOpen();
}

int G711Encoder::encode(AVFrame* frame, AVPacket* pkt)
{
	if (!frame)
	{
		return EINVAL;
	}

	int rc = 0;
	if (!isEncoderOpen())
	{
		m_profile.m_channels = frame->channels;
		m_profile.m_samplerate = frame->sample_rate;

		rc = openEncoder(m_profile);
		if (rc != 0)
		{
			return rc;
		}
	}

	if (isSamleFormat(frame->format, frame->channels, frame->sample_rate))
	{
		rc = encodeFrame(frame, pkt);
	}
	else
	{
		/// resample
		AVFramePtr outFrame(av_frame_alloc(), avframe_free);

		outFrame->format = m_codecContext->sample_fmt;
		outFrame->channels = m_codecContext->channels;
		outFrame->channel_layout = m_codecContext->channel_layout;
		outFrame->sample_rate = m_codecContext->sample_rate;
		outFrame->pts = frame->pts;

		rc = resample(frame, outFrame.get());
		if (rc > 0)
		{
			rc = encodeFrame(outFrame.get(), pkt);
		}
	}

	return rc;
}

void G711Encoder::flush()
{
	// pass
}

bool G711Encoder::getMediaFormat(MediaFormat& fmt)
{
	if (!m_profile.checkAudio())
	{
		return false;
	}

	fmt.m_audioCodec = m_profile.m_audioCodec;
	fmt.m_channels = m_profile.m_channels;
	fmt.m_sampleRate = m_profile.m_samplerate;
	fmt.m_audioBitrate = m_profile.m_audioBitrate;
	fmt.m_audioProfile = m_profile.m_audioProfile;

	return true;
}

/// 获取输入的帧格式 
bool G711Encoder::getInputFormat(int& pixelFormat, int& sampleFormat)
{
	sampleFormat = AV_SAMPLE_FMT_NONE;
	return true;
}

/// 启用硬件加速 
void G711Encoder::enableAccel(bool enabled)
{
	//
}

bool G711Encoder::setParam(const String& name, const char* value)
{
	return true;
}


int G711Encoder::openEncoder(const MediaProfile& profile)
{
    AVCodecID codecID = (AVCodecID)profile.m_audioCodec;
	AVCodec* codec = avcodec_find_encoder(codecID);
	if (!codec)
	{
		return ENOENT;
	}

	m_codecContext = avcodec_alloc_context3(codec);
	if (!m_codecContext)
	{
		return ENOENT;
	}

	m_codecContext->channels = profile.m_channels;
    m_codecContext->channel_layout = av_get_default_channel_layout(profile.m_channels);
	m_codecContext->sample_rate = profile.m_samplerate;
    m_codecContext->sample_fmt = codec->sample_fmts[0];
    m_codecContext->time_base = av_make_q(1, profile.m_samplerate);

    m_codecContext->flags |= CODEC_FLAG2_LOCAL_HEADER;

    int rc = avcodec_open2(m_codecContext, codec, NULL);

    if (rc < 0)
    {
        char buf[256];
        av_strerror(rc, buf, 256);

        av_free(m_codecContext);
        m_codecContext = NULL;

        return ENOENT;
    }

	return 0;
}

void G711Encoder::closeEncoder()
{
	if (m_swrContext)
	{
		swr_close(m_swrContext);
		swr_free(&m_swrContext);
		m_swrContext = NULL;
	}

	if (m_codecContext)
	{
		avcodec_close(m_codecContext);
		av_free(m_codecContext);
		m_codecContext = NULL;
	}
}

bool G711Encoder::isEncoderOpen()
{
	return (m_codecContext != NULL);
}

bool G711Encoder::isSamleFormat(int fmt, int channels, int sampleRate)
{
	return (m_profile.m_channels == channels) &&
        (m_profile.m_samplerate == sampleRate) &&
        (fmt == m_codecContext->sample_fmt);
}

int G711Encoder::resample(AVFrame* inFrame, AVFrame* outFrame)
{
    if (m_swrContext == NULL)
    {
        m_swrContext = swr_alloc();

        swr_config_frame(m_swrContext, outFrame, inFrame);

        swr_init(m_swrContext);
    }
    else
    {
        //int ret = swr_config_frame(m_swrContext, outFrame, inFrame);
    }

    int ret = swr_convert_frame(m_swrContext, outFrame, inFrame);

    return outFrame->nb_samples;
}

int G711Encoder::encodeFrame(AVFrame* frame, AVPacket* pkt)
{
	int got_packet = 0;
	int rc = avcodec_encode_audio2(m_codecContext, pkt, frame, &got_packet);
	if (rc == 0)
	{
		if (got_packet)
		{
			pkt->duration = frame->nb_samples;
			pkt->pos = comn::DateTime::now().totalMillisecond();
		}

		rc = got_packet ? 0 : EAGAIN;
	}

	return rc;
}



} /* namespace av */
